/**
 * 
 */
package edu.umd.clip.lm.model.data;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;

/**
 * @author Denis Filimonov <den@cs.umd.edu>
 *
 */
public class BufferedWritableChannel implements WritableByteChannel {
	private final WritableByteChannel output;
	private ByteBuffer buffer;
	private final int bufferSize;
	public static final int DEFAULT_BUFFER_SIZE = 1024*1024;
	
	public BufferedWritableChannel(WritableByteChannel output) {
		this(output, DEFAULT_BUFFER_SIZE);
	}
	
	public BufferedWritableChannel(WritableByteChannel output, int bufferSize) {
		this.output = output;
		this.bufferSize = bufferSize;
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
	 */
	@Override
	public synchronized int write(ByteBuffer src) throws IOException {
		if (buffer == null) {
			buffer = ByteBuffer.allocate(bufferSize);
		}
		
		int bytesWritten = 0;
		while(src.hasRemaining()) {
			if (buffer.remaining() < src.remaining()) {
				// flush the buffer
				buffer.flip();
				while(buffer.hasRemaining()) {
					output.write(buffer);
				}
				buffer.clear();
				
				// write from src directly
				bytesWritten += output.write(src);
			} else {
				bytesWritten += src.remaining();
				buffer.put(src);
			}
		}
		return bytesWritten;
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.Channel#close()
	 */
	@Override
	public synchronized void close() throws IOException {
		if (buffer != null) {
			// flush the buffer
			buffer.flip();
			while(buffer.hasRemaining()) {
				output.write(buffer);
			}
			buffer = null;
		}
		output.close();
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.Channel#isOpen()
	 */
	@Override
	public synchronized boolean isOpen() {
		return output.isOpen();
	}

}
